#ifndef ACCEPTOR_HPP
#define ACCEPTOR_HPP

// 输出
#include <iostream>
#include <sstream>
// inet地址
#include <arpa/inet.h>     
// 套接字
#include <sys/types.h> 
#include <sys/socket.h>
// 文件操作
#include <unistd.h>
// 字符串
#include <string>
#include <cstring>  // memset

using std::string;
using std::cout;
using std::endl;
// using std::unique_ptr;
// using std::shared_ptr;


// NoCopy 类 -------------------------------------------------------------
// 禁用拷贝构造函数和赋值运算符
// 该类的派生类默认进行不能拷贝构造和赋值（除非派生类主动实现拷贝构造和赋值运算符函数，但是不建议）
class NoCopy{
public:
    NoCopy() = default;
    ~NoCopy() = default;
    NoCopy(const NoCopy &) = delete;
    NoCopy &operator=(const NoCopy &) = delete;
};

// InetAddress 类 -------------------------------------------------------------
// 封装所有地址相关操作
class InetAddress{
public:
    InetAddress(const string &ip, unsigned short port);
    InetAddress(const struct sockaddr_in &addr);
    ~InetAddress();
    string getIp() const;            // 返回点分十进制的ip地址
    unsigned short getPort() const;  // 返回主机序的端口号
    const struct sockaddr_in *getInetAddressPtr() const;  // 返回sockaddr_in类型的指针（pointer to constant）
    const sockaddr *get_sockaddrPtr() const;     // 返回sockaddr类型的指针（pointer to constant）

private:
    struct sockaddr_in _addr;
};

inline
InetAddress::InetAddress(const string &ip, unsigned short port){
    memset(&_addr, 0, sizeof(_addr));
    _addr.sin_family = AF_INET;
    _addr.sin_addr.s_addr = inet_addr(ip.c_str());
    _addr.sin_port = htons(port);
}

inline
InetAddress::InetAddress(const struct sockaddr_in &addr){
    _addr = addr;
}

inline
InetAddress::~InetAddress(){
    // do nothing
}

inline
string InetAddress::getIp() const{
    return inet_ntoa(_addr.sin_addr);
}

inline
unsigned short InetAddress::getPort() const{
    return ntohs(_addr.sin_port);
}

inline
const struct sockaddr_in* InetAddress::getInetAddressPtr() const{
    return &_addr;
}

inline
const sockaddr* InetAddress::get_sockaddrPtr() const{
    return (struct sockaddr *)&_addr;
}

// Socket 类 -------------------------------------------------------------
// 封装所有文件描述符相关操作
// 该类体现了RAII思想
class Socket : public NoCopy{
public:
    Socket();
    explicit Socket(int fd);
    ~Socket();
    int getFd() const;

private:
    int _fd;
};

inline
Socket::Socket(){
    _fd = ::socket(AF_INET, SOCK_STREAM, 0);  // 创建内核socket对象
    if(_fd < 0){
        throw "socket create error";
    }
}

inline
Socket::Socket(int fd)
: _fd(fd)  // 绑定已有的fd
{}

inline
Socket::~Socket(){
    close(_fd);
#if DEBUG
    cout << "[debug] socket fd: " << _fd << " close" << endl;
#endif  // DEBUG
}

inline
int Socket::getFd() const{
    return _fd;
}


// Acceptor 类 -------------------------------------------------------------
// 封装服务端为socket绑定地址、开启监听等连接准备操作
class Acceptor{
    friend class EventLoop;
public:
    Acceptor(const string &ip, unsigned short port);
    ~Acceptor();
    void ready();  // 连接准备
    int accept();  // 获取一个已建立的连接

private:
    void setReuseAddr();  // 设置地址复用
    void setReusePort();  // 设置端口复用
    void bind();
    void listen();

private:
    Socket _sock;
    InetAddress _addr;
};

inline
Acceptor::Acceptor(const string &ip, unsigned short port)
: _sock(), _addr(ip, port)
{}

inline
Acceptor::~Acceptor(){
    // do nothing
}

inline
void Acceptor::ready(){
    setReuseAddr();
    setReusePort();
    bind();
    listen();
    cout << "acceptor is listening..." << endl;
}

inline
int Acceptor::accept(){
    int connFd = ::accept(_sock.getFd(), NULL, NULL);
    if(connFd < 0){
        throw "accept error";
        // perror("accept");
        // return -1;
    }
    return connFd;
}

inline
void Acceptor::setReuseAddr(){
    int opt = 1;
    int ret = setsockopt(_sock.getFd(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if(ret < 0){
        throw "setsockopt error in setReuseAddr";
    }
}

inline
void Acceptor::setReusePort(){
    int opt = 1;
    int ret = setsockopt(_sock.getFd(), SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    if(ret < 0){
        throw "setsockopt error in setReusePort";
    }
}

inline
void Acceptor::bind(){
    int ret = ::bind(_sock.getFd(), _addr.get_sockaddrPtr(), sizeof(struct sockaddr_in));
    if(ret < 0){
        throw "bind error";
    }
}

inline
void Acceptor::listen(){
    int ret = ::listen(_sock.getFd(), 128);
    if(ret < 0){
        throw "listen error";
    }
}


#endif // ACCEPTOR_HPP